package com.kaifamiao.month05.day09;

import lombok.Data;

import java.io.*;
import java.nio.file.NoSuchFileException;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Data
public class FileCut {

    private File original; // 原文件
    private File directory; // 目标文件路径
    private int num; // 线程个数
    private long size; // 单次切割的大小

    public FileCut(File original, File directory, int num, long size) {
        this.original = original;
        this.directory = directory;
        this.num = num;
        this.size = size;
    }

    public void split() throws NoSuchFileException {
        if (!original.exists() || !original.isFile()) {
            throw new NoSuchFileException("文件找不到！");
        }
        if (!directory.exists()) {
            boolean f = directory.mkdirs();
            if (!f) {
                System.err.println("创建路径失败！");
            }
        }
        long aLong = (long) Math.ceil(original.length() / (num * 1.0));  // 每个线程切割的大小
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                num, num, 10L, TimeUnit.SECONDS,
                new SynchronousQueue<>());
        long begin = 0;
        for (int i = 0; i < num; i++) {
            if (i == num - 1) { // 最后一个线程切割的大小
                aLong = original.length() - i * aLong;
            }
            pool.execute(new CutRunnable(original, directory, begin, size, aLong));
            begin += aLong;
        }
        pool.shutdown();
    }

    public static void main(String[] args) throws NoSuchFileException {
        File original = new File("E:\\1101\\temp\\test.mp4");
        File directory = new File("E:\\1101\\temp\\t");
        long size = 1024 * 1024L * 7;
        FileCut fc = new FileCut(original, directory, 5, size);
        fc.split();
    }
}

@Data
class CutRunnable implements Runnable {

    private File original; // 原文件
    private File directory; // 目标文件
    private long begin; // 开始读文件的位置
    private long size; // 单次写的大小
    private long aLong; // 当前线程读取的容量

    public CutRunnable(File original, File directory, long begin, long size, long aLong) {
        this.original = original;
        this.directory = directory;
        this.begin = begin;
        this.size = size;
        this.aLong = aLong;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "启动了！");
        RandomAccessFile rand = null;
        FileOutputStream out = null;
        try {
            rand = new RandomAccessFile(original, "r");
            rand.seek(begin);
            long arrSize = size;
            while ((arrSize = arrSize >> 1) > Integer.MAX_VALUE) ;
            byte[] bytes = new byte[(int) arrSize];
            int len; // 单次读取成功的字节
            int sum = 0; // 单次读取的字节
            long aSum = aLong; // 当前进程读取的字节
            while ((len = rand.read(bytes)) != -1) {
                if (aSum - arrSize >= 0) { // 进程剩余要写的容量
                    if (sum == 0) {
                        out = new FileOutputStream(
                                new File(directory, Thread.currentThread().getName() + System.currentTimeMillis() + ".temp"), true);
                    }
                    out.write(bytes, 0, len);
                    sum += len;
                    aSum -= len;
                    if (sum >= size) { // 单次读取的字节超过规定的字节
                        sum = 0;
                    }
                } else {
                    out.write(bytes, 0, (int) aSum); // 进程最后一次写文件
                    break;
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                close(rand, out);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    private void close(RandomAccessFile rand, OutputStream out) throws IOException {
        if (rand != null && out != null) {
            out.close();
            rand.close();
        }
    }
}